/*
 * Copyright 2012-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.context.config;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;

/**
 * 一个元素，可能直接或间接地为 {@link Environment} 提供配置数据。
 * 存在几种不同类型（{@link Kind}）的贡献者，所有类型都是不可变的，
 * 并会在处理导入（import）时被新版本替换。
 * <p>
 * 贡献者可以提供一组应被处理的导入项（import），最终这些导入项将被转换为子项。
 * 导入分为两个不同的阶段：
 * <ul>
 * <li>{@link ImportPhase#BEFORE_PROFILE_ACTIVATION 在 profile 激活之前}。</li>
 * <li>{@link ImportPhase#AFTER_PROFILE_ACTIVATION 在 profile 激活之后}。</li>
 * </ul>
 * 在每个阶段中，<em>所有</em> 导入都会在加载之前被解析。
 *
 * @author Phillip Webb
 * @author Madhura Bhave
 */
class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironmentContributor> {

	private static final ConfigData.Options EMPTY_LOCATION_OPTIONS = ConfigData.Options
			.of(ConfigData.Option.IGNORE_IMPORTS);

	private final ConfigDataLocation location;

	private final ConfigDataResource resource;

	private final boolean fromProfileSpecificImport;

	private final PropertySource<?> propertySource;

	private final ConfigurationPropertySource configurationPropertySource;

	private final ConfigDataProperties properties;

	private final ConfigData.Options configDataOptions;

	private final Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children;

	private final Kind kind;

	/**
	 * 创建一个新的 {@link ConfigDataEnvironmentContributor} 实例。
	 *
	 * @param kind 贡献者的类型
	 * @param location 此贡献者对应的配置位置
	 * @param resource 提供配置数据的资源，若无则为 {@code null}
	 * @param fromProfileSpecificImport 是否来自某个特定 profile 的导入
	 * @param propertySource 配置数据对应的属性源，若无则为 {@code null}
	 * @param configurationPropertySource 配置数据的配置属性源，若无则为 {@code null}
	 * @param properties 配置数据的属性信息，若无则为 {@code null}
	 * @param configDataOptions 应用于该贡献者的配置数据选项
	 * @param children 每个 {@link ImportPhase} 阶段下的子贡献者集合
	 */
	ConfigDataEnvironmentContributor(Kind kind, ConfigDataLocation location, ConfigDataResource resource,
			boolean fromProfileSpecificImport, PropertySource<?> propertySource,
			ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties,
			ConfigData.Options configDataOptions, Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children) {
		this.kind = kind;
		this.location = location;
		this.resource = resource;
		this.fromProfileSpecificImport = fromProfileSpecificImport;
		this.properties = properties;
		this.propertySource = propertySource;
		this.configurationPropertySource = configurationPropertySource;
		this.configDataOptions = (configDataOptions != null) ? configDataOptions : ConfigData.Options.NONE;
		this.children = (children != null) ? children : Collections.emptyMap();
	}

	/**
	 * 返回贡献者的类型。
	 * @return 贡献者的类型
	 */
	Kind getKind() {
		return this.kind;
	}

	ConfigDataLocation getLocation() {
		return this.location;
	}

	/**
	 * 返回此贡献者当前是否处于激活状态。
	 * @param activationContext 激活上下文
	 * @return 此贡献者是否处于激活状态
	 */
	boolean isActive(ConfigDataActivationContext activationContext) {
		if (this.kind == Kind.UNBOUND_IMPORT) {
			return false;
		}
		return this.properties == null || this.properties.isActive(activationContext);
	}

	/**
	 * 返回提供该实例的资源。
	 * @return 资源对象，若无则为 {@code null}
	 */
	ConfigDataResource getResource() {
		return this.resource;
	}

	/**
	 * 返回该贡献者是否来自特定 profile 的导入。
	 * @return 是否为特定 profile 的贡献者
	 */
	boolean isFromProfileSpecificImport() {
		return this.fromProfileSpecificImport;
	}

	/**
	 * 返回该贡献者的属性源。
	 * @return 属性源对象，若无则为 {@code null}
	 */
	PropertySource<?> getPropertySource() {
		return this.propertySource;
	}

	/**
	 * 返回该贡献者的配置属性源。
	 * @return 配置属性源对象，若无则为 {@code null}
	 */
	ConfigurationPropertySource getConfigurationPropertySource() {
		return this.configurationPropertySource;
	}

	/**
	 * 返回该贡献者是否包含指定的配置数据选项。
	 * @param option 要检查的选项
	 * @return 如果包含该选项，则返回 {@code true}
	 */
	boolean hasConfigDataOption(ConfigData.Option option) {
		return this.configDataOptions.contains(option);
	}

	ConfigDataEnvironmentContributor withoutConfigDataOption(ConfigData.Option option) {
		return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
				this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
				this.configDataOptions.without(option), this.children);
	}

	/**
	 * 返回该贡献者请求的所有导入项。
	 * @return 导入项列表
	 */
	List<ConfigDataLocation> getImports() {
		return (this.properties != null) ? this.properties.getImports() : Collections.emptyList();
	}

	/**
	 * 如果该贡献者在指定阶段中还有未处理的导入项，则返回 true。
	 * @param importPhase 导入阶段
	 * @return 是否存在未处理的导入项
	 */
	boolean hasUnprocessedImports(ImportPhase importPhase) {
		if (getImports().isEmpty()) {
			return false;
		}
		return !this.children.containsKey(importPhase);
	}

	/**
	 * 返回该贡献者在指定导入阶段的子贡献者列表。
	 * @param importPhase 导入阶段
	 * @return 子贡献者列表
	 */
	List<ConfigDataEnvironmentContributor> getChildren(ImportPhase importPhase) {
		return this.children.getOrDefault(importPhase, Collections.emptyList());
	}

	/**
	 * 返回一个 {@link Stream}，按优先级顺序遍历该贡献者及其所有子贡献者。
	 * @return 流对象
	 */
	Stream<ConfigDataEnvironmentContributor> stream() {
		return StreamSupport.stream(spliterator(), false);
	}

	/**
	 * 返回一个 {@link Iterator}，按优先级顺序遍历该贡献者及其所有子贡献者。
	 * @return 迭代器
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	public Iterator<ConfigDataEnvironmentContributor> iterator() {
		return new ContributorIterator();
	}

	/**
	 * 创建一个绑定了 {@link ConfigDataProperties} 的新的 {@link ConfigDataEnvironmentContributor} 实例。
	 * @param contributors 用于属性绑定的贡献者集合
	 * @param activationContext 激活上下文
	 * @return 一个新的贡献者实例
	 */
	ConfigDataEnvironmentContributor withBoundProperties(Iterable<ConfigDataEnvironmentContributor> contributors,
			ConfigDataActivationContext activationContext) {
		Iterable<ConfigurationPropertySource> sources = Collections.singleton(getConfigurationPropertySource());

		// 🧩 创建占位符解析器，用于解析配置中的 ${xxx} 占位符
		PlaceholdersResolver placeholdersResolver = new ConfigDataEnvironmentContributorPlaceholdersResolver(
				contributors, activationContext, this, true);

		// 🧵 使用 Binder 绑定配置属性，解析占位符
		Binder binder = new Binder(sources, placeholdersResolver, null, null, null);

		// ⛔ 如果启用了 遗留 配置处理，则抛出异常
		UseLegacyConfigProcessingException.throwIfRequested(binder);

		// ⚙️ 从 绑定器 获取 配置数据属性 配置对象
		ConfigDataProperties properties = ConfigDataProperties.get(binder);

		// 🚫 如果配置中指定了 IGNORE_IMPORTS 选项，则去除导入项
		if (properties != null && this.configDataOptions.contains(ConfigData.Option.IGNORE_IMPORTS)) {
			properties = properties.withoutImports();
		}

		// 🔁 返回一个新的 ConfigDataEnvironmentContributor 实例，类型为 BOUND_IMPORT
		return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.location, this.resource,
				this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, properties,
				this.configDataOptions, null);
	}

	/**
	 * 创建一个具有指定导入阶段子贡献者的新 {@link ConfigDataEnvironmentContributor} 实例。
	 * @param importPhase 导入阶段
	 * @param children 新的子贡献者列表
	 * @return 一个新的贡献者实例
	 */
	ConfigDataEnvironmentContributor withChildren(ImportPhase importPhase,
			List<ConfigDataEnvironmentContributor> children) {
		Map<ImportPhase, List<ConfigDataEnvironmentContributor>> updatedChildren = new LinkedHashMap<>(this.children);
		// 📥 将 新的子贡献者列表 插入到指定的导入阶段
		updatedChildren.put(importPhase, children);
		// 🧩 如果是 "profile 激活后" 阶段，还需调整其中的 profile-specific 结构
		if (importPhase == ImportPhase.AFTER_PROFILE_ACTIVATION) {
			// 🔀 重新整理带 profile 的 contributor 顺序
			moveProfileSpecific(updatedChildren);
		}
		// 🏗️ 返回一个新的 ConfigDataEnvironmentContributor，保持其它字段不变，仅替换 children
		return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
				this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
				this.configDataOptions, updatedChildren);
	}

	private void moveProfileSpecific(Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children) {
		// 📥 获取 配置文件激活之前 阶段的所有子配置
		List<ConfigDataEnvironmentContributor> before = children.get(ImportPhase.BEFORE_PROFILE_ACTIVATION);

		// ❌ 如果这些子配置中没有任何 profile-specific 的配置，则不需要处理，直接返回
		if (!hasAnyProfileSpecificChildren(before)) {
			return;
		}

		List<ConfigDataEnvironmentContributor> updatedBefore = new ArrayList<>(before.size());

		List<ConfigDataEnvironmentContributor> updatedAfter = new ArrayList<>();

		// 🔁 遍历 配置文件激活之前 阶段的所有子配置，把其 profile-specific 的部分移到 updatedAfter
		for (ConfigDataEnvironmentContributor contributor : before) {
			updatedBefore.add(moveProfileSpecificChildren(contributor, updatedAfter));
		}

		// 🔄 将 配置文件激活后 阶段的 贡献者 添加到 updatedAfter 的末尾，保持原有顺序
		updatedAfter.addAll(children.getOrDefault(ImportPhase.AFTER_PROFILE_ACTIVATION, Collections.emptyList()));

		// 🗂️ 更新 children Map：替换 BEFORE 和 AFTER 阶段的内容
		children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, updatedBefore);
		children.put(ImportPhase.AFTER_PROFILE_ACTIVATION, updatedAfter);
	}

	private ConfigDataEnvironmentContributor moveProfileSpecificChildren(ConfigDataEnvironmentContributor contributor,
			List<ConfigDataEnvironmentContributor> removed) {
		// 🔁 遍历所有配置导入阶段
		for (ImportPhase importPhase : ImportPhase.values()) {
			// 📥 获取当前阶段的子 贡献者 列表
			List<ConfigDataEnvironmentContributor> children = contributor.getChildren(importPhase);

			List<ConfigDataEnvironmentContributor> updatedChildren = new ArrayList<>(children.size());

			// 🔄 遍历每个子贡献者
			for (ConfigDataEnvironmentContributor child : children) {
				// 🧯 如果子贡献者 标记为 PROFILE_SPECIFIC，则将其标记移除，并放入 removed 列表
				if (child.hasConfigDataOption(ConfigData.Option.PROFILE_SPECIFIC)) {
					removed.add(child.withoutConfigDataOption(ConfigData.Option.PROFILE_SPECIFIC));
				}
				else {
					// ✅ 非 profile-specific 的直接保留
					updatedChildren.add(child);
				}
			}

			// 🔁 用更新后的 children 列表替换当前阶段的内容
			contributor = contributor.withChildren(importPhase, updatedChildren);
		}

		// 📤 返回处理后的 贡献者，profile-specific 子项已经被移除（转移到 removed 列表）
		return contributor;
	}

	private boolean hasAnyProfileSpecificChildren(List<ConfigDataEnvironmentContributor> contributors) {
		if (CollectionUtils.isEmpty(contributors)) {
			return false;
		}
		for (ConfigDataEnvironmentContributor contributor : contributors) {
			for (ImportPhase importPhase : ImportPhase.values()) {
				if (contributor.getChildren(importPhase).stream()
						.anyMatch((child) -> child.hasConfigDataOption(ConfigData.Option.PROFILE_SPECIFIC))) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 创建一个新的 {@link ConfigDataEnvironmentContributor} 实例，其中会替换已有的子节点。
	 * @param existing 要被替换的现有节点
	 * @param replacement 要替换成的新节点
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	ConfigDataEnvironmentContributor withReplacement(ConfigDataEnvironmentContributor existing,
			ConfigDataEnvironmentContributor replacement) {
		if (this == existing) {
			return replacement;
		}
		Map<ImportPhase, List<ConfigDataEnvironmentContributor>> updatedChildren = new LinkedHashMap<>(
				this.children.size());
		this.children.forEach((importPhase, contributors) -> {
			List<ConfigDataEnvironmentContributor> updatedContributors = new ArrayList<>(contributors.size());
			for (ConfigDataEnvironmentContributor contributor : contributors) {
				updatedContributors.add(contributor.withReplacement(existing, replacement));
			}
			updatedChildren.put(importPhase, Collections.unmodifiableList(updatedContributors));
		});
		return new ConfigDataEnvironmentContributor(this.kind, this.location, this.resource,
				this.fromProfileSpecificImport, this.propertySource, this.configurationPropertySource, this.properties,
				this.configDataOptions, updatedChildren);
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		buildToString("", builder);
		return builder.toString();
	}

	private void buildToString(String prefix, StringBuilder builder) {
		builder.append(prefix);
		builder.append(this.kind);
		builder.append(" ");
		builder.append(this.location);
		builder.append(" ");
		builder.append(this.resource);
		builder.append(" ");
		builder.append(this.configDataOptions);
		builder.append("\n");
		for (ConfigDataEnvironmentContributor child : this.children.getOrDefault(ImportPhase.BEFORE_PROFILE_ACTIVATION,
				Collections.emptyList())) {
			child.buildToString(prefix + "    ", builder);
		}
		for (ConfigDataEnvironmentContributor child : this.children.getOrDefault(ImportPhase.AFTER_PROFILE_ACTIVATION,
				Collections.emptyList())) {
			child.buildToString(prefix + "    ", builder);
		}
	}

	/**
	 * 创建一个 {@link Kind#ROOT 根} 贡献者的工厂方法。
	 * @param contributors 根节点的直接子贡献者列表
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	static ConfigDataEnvironmentContributor of(List<ConfigDataEnvironmentContributor> contributors) {
		Map<ImportPhase, List<ConfigDataEnvironmentContributor>> children = new LinkedHashMap<>();
		children.put(ImportPhase.BEFORE_PROFILE_ACTIVATION, Collections.unmodifiableList(contributors));
		return new ConfigDataEnvironmentContributor(Kind.ROOT, null, null, false, null, null, null, null, children);
	}

	/**
	 * 创建一个 {@link Kind#INITIAL_IMPORT 初始导入} 贡献者的工厂方法。
	 * 该贡献者用于触发额外贡献者的初始导入，自己不直接贡献任何属性。
	 * @param initialImport 已解析占位符的初始导入位置
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initialImport) {
		List<ConfigDataLocation> imports = Collections.singletonList(initialImport);
		ConfigDataProperties properties = new ConfigDataProperties(imports, null);
		return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, false, null, null, properties,
				null, null);
	}

	/**
	 * 创建一个包装已有 {@link Kind#EXISTING 现有} 属性源的贡献者的工厂方法。
	 * 该贡献者提供对现有属性的访问，但不会主动导入额外贡献者。
	 * @param propertySource 要包装的属性源
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	static ConfigDataEnvironmentContributor ofExisting(PropertySource<?> propertySource) {
		return new ConfigDataEnvironmentContributor(Kind.EXISTING, null, null, false, propertySource,
				ConfigurationPropertySource.from(propertySource), null, null, null);
	}

	/**
	 * 创建一个 {@link Kind#UNBOUND_IMPORT 未绑定导入} 贡献者的工厂方法。
	 * 该贡献者是从另一个贡献者主动导入的，并且可能会在之后导入更多贡献者。
	 * @param location 该贡献者的位置
	 * @param resource 配置数据资源
	 * @param profileSpecific 是否来自配置文件特定的导入
	 * @param configData 配置数据
	 * @param propertySourceIndex 应该使用的属性源索引
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation location, ConfigDataResource resource,
			boolean profileSpecific, ConfigData configData, int propertySourceIndex) {
		PropertySource<?> propertySource = configData.getPropertySources().get(propertySourceIndex);
		ConfigData.Options options = configData.getOptions(propertySource);
		ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource);
		return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, location, resource, profileSpecific,
				propertySource, configurationPropertySource, null, options, null);
	}

	/**
	 * 创建一个 {@link Kind#EMPTY_LOCATION 空位置} 贡献者的工厂方法。
	 * @param location 该贡献者的位置
	 * @param profileSpecific 是否来自配置文件特定的导入
	 * @return 一个新的 {@link ConfigDataEnvironmentContributor} 实例
	 */
	static ConfigDataEnvironmentContributor ofEmptyLocation(ConfigDataLocation location, boolean profileSpecific) {
		return new ConfigDataEnvironmentContributor(Kind.EMPTY_LOCATION, location, null, profileSpecific, null, null,
				null, EMPTY_LOCATION_OPTIONS, null);
	}

	/**
	 * 各种类型的贡献者。
	 */
	enum Kind {

		/**
		 * 根贡献者，用于包含初始的一组子贡献者。
		 */
		ROOT,

		/**
		 * 需要处理的初始导入。
		 */
		INITIAL_IMPORT,

		/**
		 * 现有的属性源，贡献属性但不导入其他内容。
		 */
		EXISTING,

		/**
		 * 从另一个贡献者导入了 {@link ConfigData}，但尚未绑定的贡献者。
		 */
		UNBOUND_IMPORT,

		/**
		 * 从另一个贡献者导入并且已绑定的 {@link ConfigData} 贡献者。
		 */
		BOUND_IMPORT,

		/**
		 * 有效的位置，但没有任何内容可以加载。
		 */
		EMPTY_LOCATION;

	}

	/**
	 * 获取导入时可用的导入阶段。
	 */
	enum ImportPhase {

		/**
		 * 配置文件激活之前的阶段。
		 */
		BEFORE_PROFILE_ACTIVATION,

		/**
		 * 配置文件激活之后的阶段。
		 */
		AFTER_PROFILE_ACTIVATION;

		/**
		 * 根据给定的激活上下文返回对应的 {@link ImportPhase}。
		 * @param activationContext 激活上下文
		 * @return 导入阶段
		 */
		static ImportPhase get(ConfigDataActivationContext activationContext) {
			if (activationContext != null && activationContext.getProfiles() != null) {
				return AFTER_PROFILE_ACTIVATION;
			}
			return BEFORE_PROFILE_ACTIVATION;
		}

	}

	/**
	 * 遍历贡献者树的迭代器。
	 */
	private final class ContributorIterator implements Iterator<ConfigDataEnvironmentContributor> {

		private ImportPhase phase;

		private Iterator<ConfigDataEnvironmentContributor> children;

		private Iterator<ConfigDataEnvironmentContributor> current;

		private ConfigDataEnvironmentContributor next;

		private ContributorIterator() {
			this.phase = ImportPhase.AFTER_PROFILE_ACTIVATION;
			this.children = getChildren(this.phase).iterator();
			this.current = Collections.emptyIterator();
		}

		@Override
		public boolean hasNext() {
			return fetchIfNecessary() != null;
		}

		@Override
		public ConfigDataEnvironmentContributor next() {
			ConfigDataEnvironmentContributor next = fetchIfNecessary();
			if (next == null) {
				throw new NoSuchElementException();
			}
			this.next = null;
			return next;
		}

		private ConfigDataEnvironmentContributor fetchIfNecessary() {
			if (this.next != null) {
				return this.next;
			}
			if (this.current.hasNext()) {
				this.next = this.current.next();
				return this.next;
			}
			if (this.children.hasNext()) {
				this.current = this.children.next().iterator();
				return fetchIfNecessary();
			}
			if (this.phase == ImportPhase.AFTER_PROFILE_ACTIVATION) {
				this.phase = ImportPhase.BEFORE_PROFILE_ACTIVATION;
				this.children = getChildren(this.phase).iterator();
				return fetchIfNecessary();
			}
			if (this.phase == ImportPhase.BEFORE_PROFILE_ACTIVATION) {
				this.phase = null;
				this.next = ConfigDataEnvironmentContributor.this;
				return this.next;
			}
			return null;
		}

	}

}
